﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using TreeViewEx;

namespace SpeedTest
{
	public partial class MainForm : Form
	{
		private Data data = new Data();
		private List<TreeItem> expandedNodes = new List<TreeItem>();
		private TreeItem selectedNode = new TreeItem();
		private Mode currentMode = Mode.NormalMode;
		private string currentPlanID = String.Empty;	//used only for execution mode
		private string currentEnvironment = String.Empty;
		private string currentRelease = String.Empty;
		private Filter currentFilter = null;

		public MainForm()
		{
			InitializeComponent();
		}

		private void MainForm_Load(object sender, EventArgs e)
		{
			EnableInput(false);

            if (Properties.Settings.Default.SaveWindowSize)
            {
                Height = Properties.Settings.Default.WindowHeight;
                Width = Properties.Settings.Default.WindowWidth;
            }

			cmboMenuRelease.ComboBox.ValueMember = "Value";
			cmboMenuRelease.ComboBox.DisplayMember = "Text";
			pnlFilter.Visible = false;

            if (!String.IsNullOrEmpty(Properties.Settings.Default.DBURL))
            {
                if (Properties.Settings.Default.AutoLogin)
                {
                    if (!String.IsNullOrEmpty(Properties.Settings.Default.UserName) && !String.IsNullOrEmpty(Properties.Settings.Default.Password))
                    {
                        Login(Properties.Settings.Default.UserName, Properties.Settings.Default.Password);
                    }
                }
            }
            else //display preferences form.
            {
                MessageBox.Show(this, "You must specify the location of the Speed Test database.\nPlease contact your administrator if you do not have this information.", "Error");
                PreferencesForm prefForm = new PreferencesForm(data);
                prefForm.ShowDialog(this);
            }
		}

		private void treeNavigation_NormalMode_AfterSelect(object sender, TreeViewEventArgs e)
		{
			TreeItem ti  = (TreeItem)e.Node.Tag;
			ClearPreviousPanel();

			switch (ti.ItemType)
			{
				case NodeType.CaseNode:
					PanelCase casePanel = new PanelCase(data, ti.ID, false);
					casePanel.Save += new PanelCase.OnSave(Panel_Save);
					casePanel.Dock = DockStyle.Fill;
					splitMain.Panel2.Controls.Add(casePanel);
					break;
				case NodeType.PlanNode:
					PanelPlan planPanel = new PanelPlan(data, ti.ID, false);
					planPanel.Save += new PanelPlan.OnSave(Panel_Save);
					planPanel.Dock = DockStyle.Fill;
					splitMain.Panel2.Controls.Add(planPanel);
					break;
				case NodeType.SuiteNode:
					PanelSuite suitePanel = new PanelSuite(data, ti.ID, false);
					suitePanel.Save += new PanelSuite.OnSave(Panel_Save);
					suitePanel.Dock = DockStyle.Fill;
					splitMain.Panel2.Controls.Add(suitePanel);
					break;
				default:
					break;
			}

			selectedNode = ti;
		}

		private void treeNavigation_ExecMode_AfterSelect(object sender, TreeViewEventArgs e)
		{
			//TODO: need a way to get the current environment for execution - was using ci.value
			/*
			ToolStripMenuItem menuItem = (ToolStripMenuItem)sender;
			if (menuItem.Tag != null)
			{
				ComboItem ci = (ComboItem)menuItem.Tag;
				if (treeNavigation.SelectedNode == null)
				{
					MessageBox.Show("No executable item has been selected");
					return;
				}
			*/
				TreeItem ti = (TreeItem)treeNavigation.SelectedNode.Tag;


				ClearPreviousPanel();

				switch (ti.ItemType)
				{
					case NodeType.CaseNode:
						{
							PanelExecute execPanel;
							execPanel = new PanelExecute(data, ti.ID, GetSelectedParentPlan(), currentEnvironment);
                            execPanel.Navigate += new NavigateEvent(execPanel_Navigate);
                            execPanel.SaveClicked +=new SaveEvent(execPanel_SaveClicked);
							execPanel.Dock = DockStyle.Fill;
							splitMain.Panel2.Controls.Add(execPanel);
						}
						break;
					case NodeType.PlanNode:
						{
							PanelExecuteSuitePlan execPanel;
							execPanel = new PanelExecuteSuitePlan(data, ti.ID, NodeType.PlanNode);
                            execPanel.Navigate += new NavigateEvent(execPanel_Navigate);
							execPanel.Dock = DockStyle.Fill;
							splitMain.Panel2.Controls.Add(execPanel);
						}
						break;
					case NodeType.SuiteNode:
						{
							PanelExecuteSuitePlan execPanel;
							execPanel = new PanelExecuteSuitePlan(data, ti.ID, NodeType.SuiteNode);
                            execPanel.Navigate += new NavigateEvent(execPanel_Navigate);
							execPanel.Dock = DockStyle.Fill;
							splitMain.Panel2.Controls.Add(execPanel);
						}
						break;
					default:
						MessageBox.Show("The item type you have selected is not an executable item");
						return;
				}	
		}

		void  execPanel_Navigate(NavigationDirection direction)
		{
			if (treeNavigation.SelectedNode != null)
			{
				switch (direction)
				{
                    case NavigationDirection.Previous:
                        {
                            TreeNode prevNode = FindPreviousNode(treeNavigation.SelectedNode);
                            if (prevNode != null) { treeNavigation.SelectedNode = prevNode; }
                        }
                        break;
					case NavigationDirection.Next:
                        {
                            TreeNode nextNode = FindNextNode(treeNavigation.SelectedNode, treeNavigation.SelectedNode);
                            if (nextNode != null) { treeNavigation.SelectedNode = nextNode; }
                        }
                        break;
					default:
						break;
				}
			}
		}

        private TreeNode FindPreviousNode(TreeNode node)
        {
            if (node != null)
            {
                if (node.PrevNode != null)
                {
                    return FindDeepestChild(node.PrevNode);
                }
                else
                {
                    if (node.Parent != null)
                    {
                        return node.Parent;
                    }
                }
            }

            return node;
        }

        private TreeNode FindDeepestChild(TreeNode node)
        {
            if (node != null && node.Nodes.Count > 0)
            {
                return FindDeepestChild(node.LastNode);
            }
            else { return node; }

        }

		private TreeNode FindNextNode(TreeNode node, TreeNode prevNode)
		{
			if (node != null && prevNode != null)
			{
				if (node.Nodes.Count > 0 && node != prevNode.Parent)
				{
                    return node.FirstNode;
				}
				else
				{
                    if (node.NextNode != null) { return node.NextNode; }

                    if (node.Parent != null)
                    {
                       return FindNextNode(node.Parent, node);
                    }
				}
			}

			return null;
		}

		void  execPanel_SaveClicked(string result)
		{
			if (!String.IsNullOrEmpty(result))
			{
				treeNavigation.SelectedNode.BackColor = GetResultColor(result);
			}
		}

		private void ClearPreviousPanel()
		{
			if (splitMain.Panel2.Controls.Count > 0)
			{
				foreach (Control control in splitMain.Panel2.Controls)
				{
					control.Dispose();
				}
			}

			splitMain.Panel2.Controls.Clear();
		}

		void Panel_Save(string itemID, NodeType nodeType)
		{
            selectedNode.ID = itemID;
            selectedNode.ItemType = nodeType;

			RefreshTree();
		}

		void Panel_Cancel()
		{
			RefreshTree();
		}

		private void mnuConnect_Click(object sender, EventArgs e)
		{
			Connect();
		}



		private void EnableInput(bool enable)
		{
            mnuConnect.Enabled = !enable;    //disable when enabling UI for input
            splitMain.Enabled = enable;
            toolStrip1.Enabled = enable;
            mnuTools.Enabled = enable;
            mnuReports.Enabled = enable;
            mnuAdmin.Enabled = enable;

            if (data.User.Environments.View) { }
		}

        private void EnableExecuteInput(bool enable)
        {
            cmboMenuRelease.Enabled = enable;
            btnAdd.Enabled = enable;
            btnRemove.Enabled = enable;
            btnSearch.Enabled = enable;
            btnBulkAssign.Enabled = enable;

            btnFilter.Enabled = !enable;
        }

        private void EnableInputByPermissions()
        {
            if (data != null)
            {
                if (data.User.Plans.Delete) 
				{ 
					btnRemove.Enabled = true;
					mnuDelete.Enabled = true;
				}
                else 
				{ 
					btnRemove.Enabled = false;
					mnuDelete.Enabled = false;
				}
                if (data.User.Plans.Create) 
				{ 
					btnAdd.Enabled = true;
					mnuInsert.Enabled = true;
				}
                else 
				{ 
					btnAdd.Enabled = false;
					mnuInsert.Enabled = false;
				}
                if (data.User.Plans.Edit) 
				{
					mnuMoveTo.Enabled = mnuCopyTo.Enabled = mnuDuplicate.Enabled = true;
					btnBulkAssign.Enabled = btnExecute.Enabled = true; //removed btnFilter because this is not enabled except on execute.
				}
                else 
				{
					mnuMoveTo.Enabled = mnuCopyTo.Enabled = mnuDuplicate.Enabled = false;
					btnFilter.Enabled = btnBulkAssign.Enabled = btnExecute.Enabled = false; 
				}
                if (data.User.Plans.View) { btnSearch.Enabled = true; }
                else { btnSearch.Enabled = false; }

                if (data.User.Environments.View) { mnuAdminEnviron.Enabled = true; }
                else { mnuAdminEnviron.Enabled = false; }
                if (data.User.Releases.View) { mnuAdminReleases.Enabled = true; }
                else { mnuAdminReleases.Enabled = false; }
                if (data.User.Roles.View) { mnuAdminRoles.Enabled = true; }
                else { mnuAdminRoles.Enabled = false; }
                if (data.User.Server.View) { mnuAdminServerOpt.Enabled = true; }
                else { mnuAdminServerOpt.Enabled = false; }
                if (data.User.Users.View) { mnuAdminUsers.Enabled = true; }
                else { mnuAdminUsers.Enabled = false; }
            }
        }

		private bool Connect()
		{
			LoginForm loginForm = new LoginForm();

			DialogResult res = loginForm.ShowDialog(this);

			if (res == DialogResult.OK)
			{
				return Login(loginForm.UserName, loginForm.Password);
			}
			else
			{
				return false;
			}
		}

		private bool Login(string userName, string password)
		{
            string dbURL = "127.0.0.1"; //default
			string dbPort = "5432"; //default port
            if(!String.IsNullOrEmpty(Properties.Settings.Default.DBURL)){dbURL = Properties.Settings.Default.DBURL;}
			if(!String.IsNullOrEmpty(Properties.Settings.Default.DBPort)){dbPort = Properties.Settings.Default.DBPort;}
            data.ConnectionString = "Server=" + dbURL + ";Port=" + dbPort + ";User Id=speedtestadmin;Password=speedtestadmin;Database=speedtest;";

			if (data.Connect())
			{
				if (data.Authenticate(userName, password))
				{
                    EnableInput(true);
					EnableExecuteInput(true);
                    EnableInputByPermissions();
                    RefreshAll();
				}
				else
				{
					MessageBox.Show(this, "You have entered an invalid username or password. Please try again.", "Login Error");
					return false;
				}
			}
			else { return false; }

			return true;
		}

		private void PopulateReleases()
		{
			cmboMenuRelease.ComboBox.Items.Clear();

			if (data.State == ConnectionState.Connected)
			{
				DataTable dt = data.GetActiveReleases();

				if (dt.Rows.Count > 0)
				{
					for (int i = 0; i < dt.Rows.Count; i++)
					{
						ComboItem ci = new ComboItem();
						ci.Value = dt.Rows[i]["ID"].ToString();
						ci.Text = dt.Rows[i]["Description"].ToString();
						cmboMenuRelease.ComboBox.Items.Add(ci);
					}

				}
				else
				{
					return;
				}

				if (!String.IsNullOrEmpty(Properties.Settings.Default.ActiveRelease))
				{
					cmboMenuRelease.Text = Properties.Settings.Default.ActiveRelease;
				}
				else
				{
					cmboMenuRelease.ComboBox.SelectedIndex = 0;
				}
			}

		}

		private void PopulateEnvironments()
		{
			btnExecute.DropDownItems.Clear();

			if (data.State == ConnectionState.Connected)
			{
				DataTable dt = data.GetEnvironments();

				if (dt.Rows.Count > 0)
				{
					for (int i = 0; i < dt.Rows.Count; i++)
					{
						ComboItem ci = new ComboItem();
						ci.Value = dt.Rows[i]["ID"].ToString();
						ci.Text = dt.Rows[i]["Description"].ToString();
						ToolStripMenuItem menuItem = new ToolStripMenuItem(ci.Text);
						menuItem.Tag = ci;
						menuItem.Click += btnRun_Click;
						this.btnExecute.DropDownItems.Add(menuItem);
						//cmboMenuEnvironment.ComboBox.Items.Add(ci);
					}

				}
				else
				{
					return;
				}
			}
		}

		private void PopulatePlans(string releaseID, string planID)
		{
			if (data.State == ConnectionState.Connected)
			{
				DataTable dt = null;

				if (planID != String.Empty)
				{
					dt = data.GetPlanByID(planID);
				}
				else
				{
					dt = data.GetPlansByRelease(releaseID);
				}

				if (dt.Rows.Count > 0)
				{
					for (int i = 0; i < dt.Rows.Count; i++)
					{
						//add tree items
						TreeNode tn = new TreeNode(dt.Rows[i]["ReferenceNumber"].ToString() + " " + dt.Rows[i]["Description"].ToString());
						TreeItem ti = new TreeItem();
						ti.ID = dt.Rows[i]["ID"].ToString();
						ti.ItemType = NodeType.PlanNode;
						tn.Tag = ti;
						tn.ImageIndex = 0;
						tn.SelectedImageIndex = 0;
						treeNavigation.Nodes.Add(tn);

						PopulateCaseSuites(ti.ID);
					}
				}
				else
				{
					return;
				}
			}
		}

		private void PopulateCaseSuites(string planID)
		{
			if (data.State == ConnectionState.Connected)
			{
				Filter filter = null;
				DataTable dt = null;

				if (currentFilter != null) { filter = currentFilter; }
				else { filter = new Filter(); }
				filter.Plan = planID;
				filter.Environment = currentEnvironment;

				if (currentMode == Mode.ExecuteMode)
				{
					dt = data.GetCaseSuiteResultsByFilter(filter);
				}
				else
				{
					dt = data.GetCaseSuitesByFilter(filter);
				}


				TreeNode currNode = null;

				if (dt.Rows.Count > 0)
				{
					for (int i = 0; i < dt.Rows.Count; i++)
					{
						//add tree items
						TreeNode tn = new TreeNode(dt.Rows[i]["Description"].ToString());
						TreeItem ti = new TreeItem();
						ti.ID = dt.Rows[i]["ID"].ToString();
						ti.ItemType = (NodeType)Int32.Parse(dt.Rows[i]["NodeType"].ToString());
						ti.Path = dt.Rows[i]["Path"].ToString();
						tn.Tag = ti;

						if (ti.ItemType == NodeType.CaseNode)
						{
							tn.ImageIndex = 2;
							tn.SelectedImageIndex = 2;
						}
						else
						{
							tn.ImageIndex = 1;
							tn.SelectedImageIndex = 1;
						}

						TreeNode parentNode = FindParent(dt.Rows[i]["ParentPlanID"].ToString(), dt.Rows[i]["Path"].ToString(), currNode);
						if (parentNode != null) { parentNode.Nodes.Add(tn); }

						if (currentMode == Mode.ExecuteMode)
						{
							tn.BackColor = GetResultColor(dt.Rows[i]["Result"].ToString());
						}

						currNode = tn;
					}

				}
				else
				{
					return;
				}
			}

		}

		public TreeNode FindParent(string parentID, string path, TreeNode currNode)
		{
			if (currNode == null || path.Length == 3) { return FindPlanParent(parentID); }
			else
			{
				TreeItem ti = (TreeItem)currNode.Tag;

				if (path.Length > ti.Path.Length)
				{
					//it's a child of the currNode
					return currNode;
				}

				if (path.Length == ti.Path.Length)
				{
					//it's a sibling of the currChild
					return currNode.Parent;
				}

				if (path.Length < ti.Path.Length)
				{
					//move up
					return FindParent(parentID, path, currNode.Parent);
				}
			}

			return null;
		}

		private TreeNode FindPlanParent(string parentID)
		{
			for (int i = 0; i < treeNavigation.Nodes.Count; i++)
			{
				TreeNode node = treeNavigation.Nodes[i];
				TreeItem ti = (TreeItem)node.Tag;

				if (ti.ID == parentID)
				{
					return node;
				}
			}

			return null; //not found
		}

		private void cmboMenuRelease_SelectedIndexChanged(object sender, EventArgs e)
		{
			ComboItem ci = (ComboItem)cmboMenuRelease.SelectedItem;

			Properties.Settings.Default.ActiveRelease = ci.Text;
			Properties.Settings.Default.ActiveReleaseID = ci.Value;

			expandedNodes.Clear();
			RefreshTree();
		}
	
		private void RefreshTree()
		{
			expandedNodes.Clear();
			SaveExpandedNodes(treeNavigation.Nodes);

			treeNavigation.Nodes.Clear();
			splitMain.Panel2.Controls.Clear();

			ComboItem ci = (ComboItem)cmboMenuRelease.SelectedItem;

			if (ci != null)
			{
				if (currentMode == Mode.ExecuteMode)
				{
					PopulatePlans(ci.Value, currentPlanID);
				}
				else
				{
					PopulatePlans(ci.Value, String.Empty);
				}

				ExpandNodes(treeNavigation.Nodes);
			}
		}

		private void SetNodeColors(string envID, string planID)
		{
			if (data != null)
			{
				DataTable table = data.GetResultsByPlanID(planID, envID);

				if (table != null)
				{
					for (int i = 0; i < table.Rows.Count; i++)
					{
						TreeNode node = FindCaseNode(table.Rows[i]["CaseID"].ToString(), treeNavigation.Nodes);
						if (node != null)
						{
							node.BackColor = GetResultColor(table.Rows[i]["Result"].ToString());
						}
					}
				}
			}
		}

		private Color GetResultColor(string result)
		{
			if (!String.IsNullOrEmpty(result))
			{
				switch (result.ToLower())
				{
					case "blocked":
						if (Properties.Settings.Default.BlockedColor != null) { return Properties.Settings.Default.BlockedColor; }
						else { return Color.FromArgb(252, 239, 95); }	//light yellow}
					case "failed":
						if (Properties.Settings.Default.FailColor != null) { return Properties.Settings.Default.FailColor; }
						else { return Color.FromArgb(255,127,131); }//light red
					case "passed":
						if (Properties.Settings.Default.PassColor != null) { return Properties.Settings.Default.PassColor; }
						else { return Color.FromArgb(86,255,95); }	//light green
					case "untestable":
						if (Properties.Settings.Default.UntestableColor != null) { return Properties.Settings.Default.UntestableColor; }
						else { return Color.FromArgb(196,196,196); }	//light grey
					case "rescheduled":
						if (Properties.Settings.Default.RescheduledColor != null) { return Properties.Settings.Default.RescheduledColor; }
						else { return Color.FromArgb(196, 196, 196); }
				}
			}

			return Color.White;
		}

		private TreeNode FindCaseNode(string caseID, TreeNodeCollection searchNodes)
		{
			if (searchNodes != null)
			{
				foreach (TreeNode node in searchNodes)
				{
					TreeItem ti = (TreeItem)node.Tag;
					if (ti.ID.Equals(caseID))
					{
						return node;
					}

					if (node.Nodes.Count > 0)
					{
						return FindCaseNode(caseID, node.Nodes);
					}
				}	
			}

			return null;	//if we get here, it wasn't found.
		}

		private void mnuAdminReleases_Click(object sender, EventArgs e)
		{
			AdminRelease admin = new AdminRelease(data);
			admin.ShowDialog(this);
		}

		private void environmentsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			AdminEnvironment admin = new AdminEnvironment(data);
			admin.ShowDialog(this);
		}

		private void mnuAdminUsers_Click(object sender, EventArgs e)
		{
			AdminUser admin = new AdminUser(data);
			admin.ShowDialog(this);
		}

		private void mnuAdminRoles_Click(object sender, EventArgs e)
		{
			AdminRole admin = new AdminRole(data);
			admin.ShowDialog(this);
		}

		private void btnRun_Click(object sender, EventArgs e)
		{
			switch (currentMode)
			{
				case Mode.NormalMode:
					{
						ToolStripMenuItem selMenu = (ToolStripMenuItem)sender;
						ComboItem ci = (ComboItem)selMenu.Tag;
						if (ci != null) { currentEnvironment = ci.Value; }
						SwitchModes(Mode.ExecuteMode);
					}
					break;
				case Mode.ExecuteMode:
					currentEnvironment = String.Empty;
					SwitchModes(Mode.NormalMode);
					break;
			}

			

		}

		private enum Mode
		{
			ExecuteMode,
			NormalMode
		}

		private void SwitchModes(Mode targetMode)
		{
			//change the treeview click event
			//TODO: reload the tree to only contain the selected plan
			//disable all user input for non-execute items
			//change the caption on the main form
			//change the btnRun.Image

			switch (targetMode)
			{
				case Mode.NormalMode:
					treeNavigation.AfterSelect -= treeNavigation_ExecMode_AfterSelect;
					treeNavigation.AfterSelect += treeNavigation_NormalMode_AfterSelect;

					btnFilter.BackColor = SystemColors.Control;	//in case the filter was set
					currentFilter = null;
                    if (pnlFilter.Visible) 
                    { 
                        pnlFilter.Visible = false;
                        btnFilter.BackColor = SystemColors.Control;
                    }

					EnableExecuteInput(true);

					currentMode = Mode.NormalMode;
					currentPlanID = String.Empty;

					RefreshTree();	//TODO - change this function to accept arg for only the plan needed
									//TODO - Get normal mode data

					this.Text = "Speed Test";	//TODO: - change to class like AboutBox functions

					btnExecute.Image = Properties.Resources.runButtonNormal;
					btnExecute.Click -= btnRun_Click;
					PopulateEnvironments();

					

					break;
				case Mode.ExecuteMode:
					treeNavigation.AfterSelect -= treeNavigation_NormalMode_AfterSelect;
					treeNavigation.AfterSelect += treeNavigation_ExecMode_AfterSelect;

					EnableExecuteInput(false);
					
					currentMode = Mode.ExecuteMode;
					currentPlanID = GetSelectedParentPlan();

					RefreshTree();	//TODO - change this function to accept arg for only the plan needed
									//TODO - Get data with results and set background color
									//EXPAND ALL

					this.Text = "Speed Test - EXECUTION MODE";	//TODO: - change to class like AboutBox functions

					btnExecute.Image = Properties.Resources.runButtonExec;
					btnExecute.DropDownItems.Clear();
					btnExecute.Click += btnRun_Click;

					//TODO: - remove children of btnRun and set event handler to btnRun_Click of main button.
					

					break;
			}
		}

		private void mnuViewReports_Click(object sender, EventArgs e)
		{
            string errMessage = "To use this shortcut, you must specify the address under Edit->Preferences";
            if (!String.IsNullOrEmpty(Properties.Settings.Default.MainSiteURL))
            {
                try { Process.Start(Properties.Settings.Default.MainSiteURL); }
                catch (Exception ex) { MessageBox.Show(ex.Message + "\n" + errMessage); }
            }
            else
            {
                MessageBox.Show(errMessage);
            }
		}

		private void mnuAbout_Click(object sender, EventArgs e)
		{
			AboutBox about = new AboutBox();
			about.ShowDialog();
		}

		private void btnFilter_Click(object sender, EventArgs e)
		{
			//get tester list and populate the list box
			cmboAssignedTester.ValueMember = "Value";
			cmboAssignedTester.DisplayMember = "Text";

			if (data != null)
			{
				DataTable dt = data.GetUsers();
				FillCombo(dt, dt.Columns["ID"].Ordinal, dt.Columns["UserName"].Ordinal, cmboAssignedTester);
			}

			if (currentFilter != null)
			{
				//find and select the user
				foreach (ComboItem ci in cmboAssignedTester.Items)
				{
					if(ci.Value == currentFilter.AssignedTester){cmboAssignedTester.SelectedItem = ci;}
				}
			}

			pnlFilter.Visible = true;
		}

		private void FillCombo(DataTable table, int valueIndex, int textIndex, ComboBox combo)
		{
			if (table != null && table.Rows.Count > 0)
			{
				combo.Items.Clear();

				for (int i = 0; i < table.Rows.Count; i++)
				{
					ComboItem ci = new ComboItem();
					ci.Text = table.Rows[i][textIndex].ToString();
					ci.Value = table.Rows[i][valueIndex].ToString();

					combo.Items.Add(ci);
				}
			}
		}

		private void btnFilterApply_Click(object sender, EventArgs e)
		{
			Filter filter = new Filter();

			if (cmboAssignedTester.SelectedItem != null)
			{
				ComboItem ci = (ComboItem)cmboAssignedTester.SelectedItem;
				if (ci != null){ filter.AssignedTester = ci.Value;}
			}

			filter.Results.Clear();
			for (int i = 0; i < lstResult.CheckedItems.Count; i++)
			{
				filter.Results.Add(lstResult.CheckedItems[i].ToString());
			}
			filter.CreateResultsList();

			currentFilter = filter;

			btnFilter.BackColor = Color.Aqua;
			pnlFilter.Visible = false;
			RefreshTree();
		}

		private void btnFilterClear_Click(object sender, EventArgs e)
		{
			for (int i = 0; i < lstResult.Items.Count; i++)
			{
				lstResult.SetItemChecked(i, false);
			}

			btnFilter.BackColor = SystemColors.Control;

			if (currentFilter != null)	//don't refresh if it hasn't been set.
			{
				currentFilter = null;
				RefreshTree();
			}

			pnlFilter.Visible = false;
		}

		private void btnAddPlan_Click(object sender, EventArgs e)
		{
			PanelPlan planPanel = new PanelPlan(data, EditMode.CreateMode);
			planPanel.Save += new PanelPlan.OnSave(Panel_Save);
			planPanel.Cancel += new PanelPlan.OnCancel(Panel_Cancel);
			planPanel.Dock = DockStyle.Fill;
			splitMain.Panel2.Controls.Clear();
			splitMain.Panel2.Controls.Add(planPanel);
		}

		private void btnAddSuite_Click(object sender, EventArgs e)
		{			
			if(treeNavigation.SelectedNode == null)
			{
				MessageBox.Show("You must first select an insertion point, either a Test Plan or one of its children");
				return;
			}

			PanelSuite suitePanel = new PanelSuite(data, EditMode.CreateMode, GetSelectedParentPlan(), GetParentPath(NodeType.SuiteNode));
			suitePanel.Save += new PanelSuite.OnSave(Panel_Save);
			suitePanel.Cancel += new PanelSuite.OnCancel(Panel_Cancel);
			suitePanel.Dock = DockStyle.Fill;
			splitMain.Panel2.Controls.Clear();
			splitMain.Panel2.Controls.Add(suitePanel);
		}

		private string GetParentPlan(TreeNode node)
		{
			TreeNode selNode = node;

			while (true)
			{
				TreeItem item = (TreeItem)selNode.Tag;

				if (item.ItemType == NodeType.PlanNode)
				{
					return item.ID;
				}
				else
				{
					if (selNode.Parent != null)
					{
						selNode = selNode.Parent;
					}
					else
					{
						return String.Empty;
					}
				}
			}
		}

		private string GetSelectedParentPlan()
		{
			return GetParentPlan(treeNavigation.SelectedNode);		
		}

		private string GetParentPath(NodeType childNodeType)
		{
			//if the selected item can be a parent of the new item, return it's path, otherwise loop until a suitable parent is found.
			//only Test Cases cannot be parents

			TreeNode selNode = treeNavigation.SelectedNode;

			while (true)
			{
				TreeItem item = (TreeItem)selNode.Tag;

				if (item.ItemType != NodeType.CaseNode)
				{
					return item.Path;
				}
				else
				{
					if (selNode.Parent != null)
					{
						selNode = selNode.Parent;
					}
					else
					{
						return String.Empty;
					}
				}
			}

		}

		private void btnAddCase_Click(object sender, EventArgs e)
		{
			if (treeNavigation.SelectedNode == null)
			{
				MessageBox.Show("You must first select an insertion point, either a Test Plan or one of its children");
				return;
			}

			PanelCase casePanel = new PanelCase(data, EditMode.CreateMode, GetSelectedParentPlan(), GetParentPath(NodeType.CaseNode));
			casePanel.Save += new PanelCase.OnSave(Panel_Save);
			casePanel.Cancel += new PanelCase.OnCancel(Panel_Cancel);
			casePanel.Dock = DockStyle.Fill;
			splitMain.Panel2.Controls.Clear();
			splitMain.Panel2.Controls.Add(casePanel);
		}

		private void btnRemove_Click(object sender, EventArgs e)
		{
			if (treeNavigation.SelectedNode == null)
			{
				MessageBox.Show("No item has been selected");
				return;
			}

			TreeItem ti = (TreeItem)treeNavigation.SelectedNode.Tag;
			DialogResult res = MessageBox.Show("Are you sure you want to delete the item: " + treeNavigation.SelectedNode.Text + " and all of it's child items?", "Confirm", MessageBoxButtons.YesNo);

            //TODO: Delete subitems and renumber if needed.

			if (res == DialogResult.Yes)
			{
				switch (ti.ItemType)
				{
					case NodeType.CaseNode:
						data.DeleteCaseSuiteByID(ti.ID);
						break;
					case NodeType.PlanNode:
						data.DeletePlanByID(ti.ID);
						break;
					case NodeType.SuiteNode:
						data.DeleteCaseSuiteByID(ti.ID);
						break;
					default:
						MessageBox.Show("There was an error deleting the selected item");
						return;
				}

				RefreshTree();
			}
		}

		private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
		{
            try
            {
                Properties.Settings.Default.WindowHeight = Height;
                Properties.Settings.Default.WindowWidth = Width;
            }
            catch (NullReferenceException)
            {
            }

            Properties.Settings.Default.Save();
		}

		private void SaveExpandedNodes(TreeNodeCollection nodes)
		{
			for (int i = 0; i < nodes.Count; i++)
			{
				if (nodes[i].IsExpanded)
				{
					TreeItem expandedItem = (TreeItem)nodes[i].Tag;
					expandedNodes.Add(expandedItem);
				}

				if (nodes[i].IsSelected)
				{
					selectedNode = (TreeItem)nodes[i].Tag;
				}

				if (nodes[i].Nodes.Count > 0)
				{
					SaveExpandedNodes(nodes[i].Nodes);
				}
			}

		}

		private void ExpandNodes(TreeNodeCollection nodes)
		{
			for (int i = 0; i < nodes.Count; i++)
			{
				TreeItem item = (TreeItem)nodes[i].Tag;

				for (int j = 0; j < expandedNodes.Count; j++)
				{
					if (expandedNodes[j].ID == item.ID && expandedNodes[j].ItemType == item.ItemType)
					{
						nodes[i].Expand();
						break;
					}
				}

				if (item.ID.Equals(selectedNode.ID) && item.ItemType.Equals(selectedNode.ItemType)) 
				{ treeNavigation.SelectedNode = nodes[i]; }

				if (nodes[i].Nodes.Count > 0)
				{
					ExpandNodes(nodes[i].Nodes);
				}
			}
		}

        private void mnuAdminServerOpt_Click(object sender, EventArgs e)
        {
            AdminServer admin = new AdminServer(data);
            admin.ShowDialog(this);
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            PanelSearch searchPanel = new PanelSearch(data);
            searchPanel.Dock = DockStyle.Fill;
			searchPanel.Search += new SearchEvent(searchPanel_Search);
            splitMain.Panel2.Controls.Clear();
            splitMain.Panel2.Controls.Add(searchPanel);
        }

		void searchPanel_Search(SearchParameters searchParams)
		{
			//set release
			if(!String.IsNullOrEmpty(searchParams.ReleaseID))
			{
				int index = FindReleaseIndex(searchParams.ReleaseID);
				if (index >= 0) { cmboMenuRelease.SelectedIndex = index; }
			}
				//this should trigger a reload of the plans.
			//find plan
			if(!String.IsNullOrEmpty(searchParams.PlanID))
			{
				TreeNode planNode = FindPlanParent(searchParams.PlanID);
				if (planNode != null) { planNode.Expand(); }
			}

			if (!String.IsNullOrEmpty(searchParams.CaseSuiteID))
			{
				TreeNode caseNode = FindCaseNode(searchParams.CaseSuiteID, treeNavigation.Nodes);
				if (caseNode != null) { treeNavigation.SelectedNode = caseNode; }
			}
			//find case
		}

		private int FindReleaseIndex(string releaseID)
		{
			for (int i = 0; i < cmboMenuRelease.Items.Count; i++)
			{
				ComboItem ci = (ComboItem)cmboMenuRelease.Items[i];
				if (ci.Value.Equals(releaseID))
				{
					return i;
				}
			}
			return -1;
		}

		private void btnBulkAssign_Click(object sender, EventArgs e)
        {
			ComboItem ci = (ComboItem)cmboMenuRelease.SelectedItem;

			PanelBulkAssign bulkPanel = new PanelBulkAssign(data, currentPlanID, ci.Value);
            bulkPanel.Dock = DockStyle.Fill;
            splitMain.Panel2.Controls.Clear();
            splitMain.Panel2.Controls.Add(bulkPanel);
        }

		private void mnuPreferences_Click(object sender, EventArgs e)
		{
			PreferencesForm prefForm = new PreferencesForm(data);
			prefForm.ShowDialog();
		}

		private void mnuDuplicate_Click(object sender, EventArgs e)
		{
			//duplicate keeps any parents intact
			//including plans and suites and
			//just creates a sibling node(s)
			if (treeNavigation.SelectedNode != null)
			{
				TreeItem ti = (TreeItem)treeNavigation.SelectedNode.Tag;
				if (ti != null && ti.ItemType != NodeType.PlanNode)
				{
					data.DuplicateCaseSuiteByID(ti.ID);
					RefreshTree();
				}
			}

		}

		private void mnuCopyTo_Click(object sender, EventArgs e)
		{
            ShowMoveCopy(CopyMode.Copy);
		}

		private void mnuMoveTo_Click(object sender, EventArgs e)
		{
            ShowMoveCopy(CopyMode.Move);
		}

        private void ShowMoveCopy(CopyMode mode)
        {
            if (treeNavigation.SelectedNode != null)
            {
                TreeItem ti = (TreeItem)treeNavigation.SelectedNode.Tag;
                if (ti != null)
                {
                    MoveCopyForm moveCopy = new MoveCopyForm(data, mode, ti.ItemType, ti.ID);
                    moveCopy.ShowDialog();

                    RefreshTree();
                }
            }
        }

        private void treeNavigation_DragComplete(object sender, DragEventData e, CancelEvent cancel)
        {
			string sourcePlanID = GetParentPlan(e.SourceNode);
			if (sourcePlanID == GetParentPlan(e.TargetNode))	//this is a preliminary check. The real check occurs in Data
			{
				TreeItem source = (TreeItem)e.SourceNode.Tag;
				TreeItem target = (TreeItem)e.TargetNode.Tag;

                if (target.ItemType == NodeType.PlanNode) { return; } //this cannot be done

				DropPosition dp = e.TargetPosition;
				//can't drop IN a case node
				if (target.ItemType == NodeType.CaseNode && e.TargetPosition == DropPosition.DropIn) { dp = DropPosition.DropAbove; }
				data.MoveCaseSuiteInPlan(source.ID, target.ID, dp);

                RefreshTree();
			}

        }

        private void treeNavigation_DragStart(object sender, DragEventData e, CancelEvent cancel)
        {
            if (e.SourceNode != null)
            {
                TreeItem ti = (TreeItem)e.SourceNode.Tag;
                if (ti.ItemType == NodeType.PlanNode) { cancel.Cancel = true; }
            }
        }

		private void mnuCollapseAll_Click(object sender, EventArgs e)
		{
			treeNavigation.CollapseAll();
		}

		private void mnuExpandAll_Click(object sender, EventArgs e)
		{
			treeNavigation.ExpandAll();
		}

		private void btnRefresh_Click(object sender, EventArgs e)
		{
			//refresh everything
            RefreshAll();
		}

        private void RefreshAll()
        {
			PopulateEnvironments();
			PopulateReleases();
			RefreshTree();
        }

		private void mnuDisconnect_Click(object sender, EventArgs e)
		{
			if (data != null)
			{
				data.Disconnect();
				EnableInput(false);
			}
		}

		private void treeNavigation_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				treeNavigation.SelectedNode = treeNavigation.GetNodeAt(e.X, e.Y);
			}
		}

		private void mnuHelpContents_Click(object sender, EventArgs e)
		{
			string errMessage = "Unable to open the help file for Speed Test";
			string helpFilePath = Path.Combine(Application.StartupPath, "SpeedTestHelp.chm");
			if (File.Exists(helpFilePath))
			{
				try { Process.Start(helpFilePath); }
				catch (Exception ex) { MessageBox.Show(ex.Message + "\n" + errMessage); }
			}
			else
			{
				MessageBox.Show(errMessage);
			}
        }

        private void MainForm_FormClosing(object sender, EventArgs e)
        {
            MainForm_FormClosing(sender, new FormClosingEventArgs(CloseReason.UserClosing, false));
            Close();
        }
	}

	public enum ExpandAction
	{
		Remove,
		Add
	}

	public enum NodeType
	{
		PlanNode,	//0
		SuiteNode,	//1
		CaseNode,	//2
		Undefined	//3	
	}

	public class TreeItem
	{
		private NodeType itemType = NodeType.Undefined;
		private string id = String.Empty;
		private string path = String.Empty;
		private string result = String.Empty;
		private Color bgColor = Color.White;

		public NodeType ItemType
		{
			get
			{
				return itemType;
			}
			set
			{
				itemType = value;
			}
		}

		public string ID
		{
			get
			{
				return id;
			}
			set
			{
				id = value;
			}
		}

		public string Path
		{
			get
			{
				return path;
			}
			set
			{
				path = value;
			}
		}

		public string Result
		{
			get
			{
				return result;
			}
			set
			{
				result = value;
			}
		}

		public Color BackgroundColor
		{
			get
			{
				return bgColor;
			}
			set
			{
				bgColor = value;
			}
		}
	}

	public class ComboItem
	{
		private string itemValue = String.Empty;
		private string itemText = String.Empty;
		
		public string Value
		{
			get
			{
				return itemValue;
			}
			set
			{
				itemValue = value;
			}
		}

		public string Text
		{
			get
			{
				return itemText;
			}
			set
			{
				itemText = value;
			}
		}
	}

	public delegate void NavigateEvent(NavigationDirection direction);
	public delegate void SearchEvent(SearchParameters searchParams);
	public delegate void SaveEvent(string result);

	public enum NavigationDirection
	{
		Previous,
		Next,
		Same,
	}

	public class SearchParameters
	{
		private string releaseID = String.Empty;
		private string planID = String.Empty;
		private string caseSuiteID = String.Empty;

		public string ReleaseID
		{
			get
			{
				return releaseID;
			}
			set
			{
				releaseID = value;
			}
		}

		public string PlanID
		{
			get
			{
				return planID;
			}
			set
			{
				planID = value;
			}
		}

		public string CaseSuiteID
		{
			get
			{
				return caseSuiteID;
			}
			set
			{
				caseSuiteID = value;
			}
		}
	}
}
